package org.tool.reflect.proxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import org.tool.core.lang.Assert;
import org.tool.reflect.ClassLoaderUtil;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * JDK动态代理 和 CGLib动态代理 工厂类
 *
 * @author 李坤
 * @date 2022/4/19 14:35
 */
public abstract class ProxyFactory {

    /**
     * 生成给定接口类型的 JDK 动态代理对象
     *
     * @param interfaces        接口类型
     * @param invocationHandler 动态代理处理器
     *
     * @return 动态代理对象
     */
    public static Object getJdkProxy(Class<?>[] interfaces, InvocationHandler invocationHandler) {
        final ClassLoader classLoader = ClassLoaderUtil.getDefaultClassLoader();
        return getJdkProxy(interfaces, classLoader, invocationHandler);
    }

    /**
     * 生成给定接口类型的 JDK 动态代理对象
     *
     * @param interfaces        接口类型
     * @param classLoader       类加载器
     * @param invocationHandler 动态代理处理器
     *
     * @return 动态代理对象
     */
    public static Object getJdkProxy(Class<?>[] interfaces, ClassLoader classLoader, InvocationHandler invocationHandler) {
        Assert.nonEmpty(interfaces, "interfaces can not be empty");

        return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
    }

    /**
     * 生成给定对象的 JDK 动态代理对象
     *
     * @param target            目标对象
     * @param invocationHandler 动态代理处理器
     * @param <T>               目标对象类型
     *
     * @return 动态代理对象
     */
    public static <T> T getJdkProxy4Target(T target, InvocationHandler invocationHandler) {
        final ClassLoader classLoader = target.getClass().getClassLoader();
        return getJdkProxy4Target(target, classLoader, invocationHandler);
    }

    /**
     * 生成给定对象的 JDK 动态代理对象
     *
     * @param target            目标对象
     * @param classLoader       类加载器
     * @param invocationHandler 动态代理处理器
     * @param <T>               目标对象类型
     *
     * @return 动态代理对象
     */
    public static <T> T getJdkProxy4Target(T target, ClassLoader classLoader, InvocationHandler invocationHandler) {
        Assert.notNull(target, "target can not be null");

        final Class<?>[] interfaces = target.getClass().getInterfaces();
        return (T) getJdkProxy(interfaces, classLoader, invocationHandler);
    }

    /**
     * 生成给定类型的 CGLIB 代理对象
     *
     * @param clazz             待生成的代理对象类型
     * @param methodInterceptor CGLIB 方法拦截器
     * @param <T>               生成对象的泛型
     *
     * @return CGLIB 代理对象
     */
    public static <T> T getCglibProxy(Class<T> clazz, MethodInterceptor methodInterceptor) {
        final ClassLoader classLoader = ClassLoaderUtil.getDefaultClassLoader();
        return getCglibProxy(clazz, classLoader, methodInterceptor);
    }

    /**
     * 生成给定类型的 CGLIB 代理对象
     *
     * @param clazz             待生成的代理对象类型
     * @param classLoader       类加载器
     * @param methodInterceptor CGLIB 方法拦截器
     * @param <T>               生成对象的泛型
     *
     * @return CGLIB 代理对象
     */
    public static <T> T getCglibProxy(Class<T> clazz, ClassLoader classLoader, MethodInterceptor methodInterceptor) {
        Assert.notNull(clazz, "clazz can not be null");

        final Enhancer enhancer = new Enhancer();

        enhancer.setSuperclass(clazz);
        enhancer.setClassLoader(classLoader);
        enhancer.setCallback(methodInterceptor);

        return (T) enhancer.create();
    }

    /**
     * 生成给定对象的 CGLIB 代理对象
     *
     * @param target            待生成的代理对象
     * @param methodInterceptor CGLIB 方法拦截器
     * @param <T>               生成对象的泛型
     *
     * @return CGLIB 代理对象
     */
    public static <T> T getCglibProxy4Target(T target, MethodInterceptor methodInterceptor) {
        final ClassLoader classLoader = ClassLoaderUtil.getDefaultClassLoader();
        return (T) getCglibProxy4Target(target.getClass(), classLoader, methodInterceptor);
    }

    /**
     * 生成给定对象的 CGLIB 代理对象
     *
     * @param target            待生成的代理对象
     * @param classLoader       类加载器
     * @param methodInterceptor CGLIB 方法拦截器
     * @param <T>               生成对象的泛型
     *
     * @return CGLIB 代理对象
     */
    public static <T> T getCglibProxy4Target(T target, ClassLoader classLoader, MethodInterceptor methodInterceptor) {
        Assert.notNull(target, "target can not be null");

        return (T) getCglibProxy(target.getClass(), classLoader, methodInterceptor);
    }
}
